﻿using System;
using System.IO;
using System.Net;
using System.Text;
using System.Runtime.InteropServices;
using Net.System;
using Net.Helper;
using Net.Event;
using Udx;

namespace Net.Server
{
    /// <summary>
    /// udx服务器类型  只能300人以下连接, 如果想要300个客户端以上, 请进入udx网址:www.goodudx.com 联系作者下载专业版FastUdxApi.dll, 然后更换下框架内的FastUdxApi.dll即可
    /// 第三版本 2020.9.14
    /// <para>Player:当有客户端连接服务器就会创建一个Player对象出来, Player对象和XXXClient是对等端, 每当有数据处理都会通知Player对象. </para>
    /// <para>Scene:你可以定义自己的场景类型, 比如帧同步场景处理, mmorpg场景什么处理, 可以重写Scene的Update等等方法实现每个场景的更新和处理. </para>
    /// </summary>
    public class UdxServer<Player, Scene> : ServerUdpBase<Player, Scene> where Player : UdxPlayer, new() where Scene : SceneBase<Player>, new()
    {
        /// <summary>
        /// udx服务器对象
        /// </summary>
        public new IntPtr Server;
        private UDXPRC udxPrc;

        public override void Start(int port = 9543)
        {
            if (Server != IntPtr.Zero)//如果服务器套接字已创建
                throw new Exception("服务器已经运行，不可重新启动，请先关闭后在重启服务器");
            base.Start(port);
        }

        protected override void CreateServerSocket(int port)
        {
#if !UNITY_EDITOR && !UNITY_STANDALONE && !UNITY_ANDROID && !UNITY_IOS && WINDOWS
            var path = AppDomain.CurrentDomain.BaseDirectory;
            if (!File.Exists(PathHelper.Combine(path, "udxapi.dll")))
                throw new FileNotFoundException($"udxapi.dll没有在程序根目录中! 请从GameDesigner文件夹下找到udxapi.dll复制到{path}目录下.");
#endif
            if (!UdxLib.INIT)
            {
                UdxLib.INIT = true;
                UdxLib.UInit(Config.MaxThread);
                UdxLib.UEnableLog(false);
            }
            Server = UdxLib.UCreateFUObj();
            UdxLib.UBind(Server, null, port);
            udxPrc = new UDXPRC(ProcessReceive);
            UdxLib.USetFUCB(Server, udxPrc);
            GC.KeepAlive(udxPrc);
        }

        protected override void StartSocketHandler() { }

        protected void ProcessReceive(UDXEVENT_TYPE eventType, int error, IntPtr clientPtr, IntPtr dataPtr, int length)
        {
            try
            {
                Player client;
                IntPtr userDataPtr;
                switch (eventType)
                {
                    case UDXEVENT_TYPE.E_LINKBROKEN:
                        userDataPtr = UdxLib.UGetUserData(clientPtr);
                        var handler = GCHandle.FromIntPtr(userDataPtr);
                        client = handler.Target as Player;
                        if (client == null)
                            return;
                        client.RemoveClient();
                        break;
                    case UDXEVENT_TYPE.E_DATAREAD:
                        var buffer = BufferPool.Take(length);
                        buffer.Count = length;
                        Marshal.Copy(dataPtr, buffer.Buffer, 0, length);
                        userDataPtr = UdxLib.UGetUserData(clientPtr);
                        if (userDataPtr == IntPtr.Zero)
                        {
                            var ipbytes = new byte[128];
                            int port = 0;
                            int ntype = 0;
                            UdxLib.USetGameMode(clientPtr, true);
                            UdxLib.UGetRemoteAddr(clientPtr, ipbytes, ref port, ref ntype);
                            port = UdxLib.UGetDesStreamID(clientPtr);
                            var ipAddress = Encoding.ASCII.GetString(ipbytes, 0, 128);
                            ipAddress = ipAddress.Replace("\0", "");
                            var remotePoint = new IPEndPoint(IPAddress.Parse(ipAddress), port);
                            client = CheckReconnect(remotePoint, buffer, clientPtr);
                            var handle = GCHandle.Alloc(client);
                            var ptr = GCHandle.ToIntPtr(handle);
                            UdxLib.USetUserData(clientPtr, ptr.ToInt64());
                            client.Handle = handle;
                            return;
                        }
                        client = GCHandle.FromIntPtr(userDataPtr).Target as Player;
                        if (client == null)
                            return;
                        client.ReceiveList.Enqueue(buffer);
                        break;
                }
            }
            catch (Exception ex)
            {
                NDebug.LogError(ex);
            }
        }

        protected override void AcceptHander(Player client, params object[] args)
        {
            client.Udx = (nint)args[0];
        }

        public override void Close()
        {
            base.Close();
            if (Server != IntPtr.Zero)
            {
                UdxLib.UDestroyFUObj(Server);
                Server = IntPtr.Zero;
            }
        }

        ~UdxServer()
        {
            Close();
        }
    }

    /// <summary>
    /// 默认udx服务器，当不需要处理Player对象和Scene对象时可使用
    /// </summary>
    public class UdxServer : UdxServer<UdxPlayer, SceneBase<UdxPlayer>>
    {
    }
}